Linguaggio C

 

per collaborazioni, commenti, critiche, e altro contattateci alla e-mail: clubinfo@libero.it risponderemo al più presto!

Aggregazione dati: strutture

di Luca Sabatucci

Lezione 10

Pagina principale | Lezione precedente | Lezione successiva


Esempi di array e record

Siamo giunti al secondo pit stop; dopo avere introdotto due nuovi argomenti fondamentali come gli array e i record siamo in grado di maipolare le strutture dati per creare programmi già piuttosto complessi. Gli array si usano principalmente quando si lavora con una certà quantità di elementi tutti delle stesso tipo. I record invece servono per memorizzare dati che normalmente prevedono l'uso di più variabili per essere memorizzate. Come abbiamo già fatto dopo avere introdotto i primi argomenti del corso, mi sembra opportuno presentare alcuni listati funzionanti che è possibile provare. Li potrete scaricare direttamente in fondo alla lezione. Vi consiglio, se volete davvero imparare a programmare, di provare a modificarli, per ottenere qualche cosa di più complesso. Alla fine di ogni listato vi darò io stesso dei suggerimenti su cosa aggiungere.

Numeri primi

La prima volta avevamo presentato un metodo iterativo per determinare i numeri primi minori di un certo numero. In quella occasione abbiamo detto che si tratta di un problema "difficile", ovvero per il quale non esiste una soluzione efficiente. Avevamo anche detto che per questo genere di problemi si deve scegliere tra efficienza di tempo o di spazio di memeoria. Noi avevamo scelto l'efficienza di spazio, con il difetto che per calcolare numeri primi grandi il programma occupava un tempo elevato.
Proviamo adesso questa seconda soluzione in cui miglioriamo l'aspetto temporale, ma finiamo per occupare una quantità di memoria piuttosto elevata.

/* esempio sugli ARRAY */
/* come calcolare i numeri primi usando un array */

#include <stdio.h>
#define MAX 100

void inizializza_tabella(int tabella[],int max);
void trova_numeri_primi(int tabella[],int max);


int main() {
	int tabella[MAX];
	int count;

	inizializza_tabella(tabella,MAX);
	trova_numeri_primi(tabella,MAX);

	/* stampa tabella */
	for (count = 0; count < MAX; count++)
		if (tabella[count] != -1)
			printf("%d\n",tabella[count]);

	return 0;
}

void inizializza_tabella(int tabella[],int max) {
	int count;

	for (count = 0; count < max; count++)
		tabella[count] = count+1;
}

void trova_numeri_primi(int tabella[], int max) {
	int count;
	int molt;

	for (count = 1; count < max; count++)
		if (tabella[count] != -1) {

			molt = 2;
			while (tabella[count] * molt <= max) {
				tabella[ tabella[count]*molt - 1] = -1;
				molt++;

			}
		}

}

In questa versione viene creato un array di numeri interi pari al numero massimo. Questa operazione crea un occupazione di memoria pari a circa 8 * MAX byte; se MAX assume il valore di 1000 si occupa uno spazio di circa 8 Kb. Data la quantità di memoria presente sui nostri PC, questo metoto risulta più efficace rispetto al primo.

Una volta creato questo array, si inizializza con i numeri interi a partire da 1, fino a MAX+1. Quindi si procede determinando dal basso se i numeri della tabella sono primi. Per ogni numero primo trovato si elidono i suoi multipli, segnando con un -1 la cella dell'array corrispondente.

Esempio: 1 2 3 4 5 6 7 8 9 10 11 ... il numero 2 viene riconosciuto come numero primo, questo provoca il cancellamento di tutti i numeri pari (due escluso naturalmente): 1 2 3 / 5 / 7 / 9 / 11 ...

Se si ripete questo procedimento per tutti i numeri primi (saltando quei numeri che sono stati già eliminati) si ottiene una tabella in cui tutti i numeri non primi sono stati eliminati. Rileggendo la tabella è un gioco da ragazzi determinare quali numeri sono primi.

Un possibile miglioramento è quello di inizializzare la tabella mettendo solo numeri dispari (2 a parte) e cominciare l'iterazione dal numero 3. In questo modo l'occupazione di spazio dimezza. Si stia attenti che quando si calcola un multiplo di un numero, se questo è un numero pari deve essere scartato (perchè non esistono numeri pari da eliminare nella tabella).

Devianza

Data una lista di numeri, tutti sappiamo cosa è la media: la somma di tutti i numeri divisi per il loro numero. La devianza è un'altra statistica che si può prelevare da una lista di numeri. Questa si calcola per ogni numero come la differenza tra il numero e la media della lista stessa. Indica quanto si distacca tale valore dal valore medio atteso.

Come si ci può rendere conto analizzando il flusso del programma, questa volta non è possibile fare a meno di memorizzare i valori introdotti in un array, come nel caso della media. Infatti per poter calcolare la differenza tra un numero inserito e la media è necessario disporre di quest'ultima. Ma la media di una lista si conosce necessariamente dopo aver insirito tutti i valori.

Per memorizzare i valori introdotti utilizziamo un array. Dimensionamo l'array con un valore grande in modo da poter ospitare quanti più numeri possibile. Poi però utilizziamo solo la parte che ci serve realmente dell'array:

/* calcolo della devianza */

#include <stdio.h>
#define MAX 100

int main() {
	double numero[MAX];
	int numeri_inseriti;
	double in;
	double somma = 0;
	double media;
	double devianza = 0;
	int count;

	printf("Quanti numeri vuoi inserire?");
	scanf("%d",&numeri_inseriti);

	for (count = 0; count<numeri_inseriti; count++) {
		scanf("%lf",&in);

		numero[count] = in;
		somma += in;
	}

	/* calcolo della media */
	media = somma / numeri_inseriti;
	printf("\nMedia: %f",media);

	/* calcolo della devianza */
	for (count = 0; count < numeri_inseriti; count++) {
		devianza = numero[count] - media;
		printf("\nDevianza: %f",devianza);
	}
}

Viene svolto tutto dentro la main: richiesta del numero di valori da inserire, e richiesta di tutti i valori. Quindi viene prima calcolata la media con l'algoritmo visto in precedenza e poi per ogni valore introdotto viene calcolata la devianza.

Numeri complessi

Un numero complesso è formato da una coppia di numeri reali, chiamati parte reale e parte immaginaria. Vediamo qui una semplice implementazione di numeri complessi e introduciamo alcune funzioni che operano su tale struttura dati.

#include <stdio.h>
#include <math.h>

/* numeri complessi */

struct numero_complesso {
	double reale;
	double immag;
};

typedef struct numero_complesso complesso;

complesso input_complesso();
void output_complesso(complesso c);
complesso somma_complesso(complesso op1, complesso op2);

int main() {
	complesso a,b,c;

	a = input_complesso();
	b = input_complesso();

	c = somma_complesso(a,b);

	output_complesso(a);
	printf("+");
	output_complesso(b);
	printf("=");
	output_complesso(c);
	printf("\n");

	return 0;
}

void output_complesso(complesso c) {
	if (c.immag > 0)
		printf("%f+j%f",c.reale,fabs(c.immag));
	else
		printf("%f-j%f",c.reale,fabs(c.immag));

}

complesso input_complesso() {
	complesso in;

	printf("Inserire il numero complesso nel formato re,im:\n");
	scanf("%lf,%lf",&in.reale,&in.immag);

	return in;
}

complesso somma_complesso(complesso op1, complesso op2) {
	complesso out;

	out.reale = op1.reale + op2.reale;
	out.immag = op1.immag + op2.immag;

	return out;
}

Questo esempio mostra un tipo uso della struttura record. Mediante un record abbiamo esteso le capacità del nostro linguaggio introducendo un tipo che non era presente tra i tipi primitivi. Su questo tipo dobbiamo anche introdurre degli operatori con i quali è giusto manipolare la struttura dati.

Questo esempio ci avvicina a quella che poi sarà la programmazione orientata ad oggetti tipica del C++. Nella sintassi dei linguaggi object-oriented complesso è l'oggetto, mentre le funzioni input_complesso, somma_complesso e output_complesso rappresentano l'interfaccia di tale oggetto.
Finchè l'accesso alla struttura dati avviene mediante tali funzioni anche il nostro programmino in C si candida ad essere un esempio di programmazione ad oggetti.

Questa divagazione mi serve a dimostrare che un linguaggio senza "regole" come il C, se usato bene può sostituire degnamente linguaggi che invece impongono uno stile di programmazione ben preciso.

Un miglioramento di questo programma può essere l'aggiunta di nuove funzioni come coniuga_complesso e moltiplica_complesso.


Listati presenti in questa lezione

  1. Numeri primi
  2. Devianza
  3. Numeri complessi

Bibliografia


Testi consigliati per l'apprendimento

Questo articolo è stato scaricato dal Club di informatica
Pagina curata da Luca Sabatucci